package com.atguigu.gmall.seckill.service.biz.impl;

import com.atguigu.gmall.common.constant.RabbitConstant;
import com.atguigu.gmall.common.constant.RedisConstant;
import com.atguigu.gmall.common.execption.GmallException;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.AuthContextHolder;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.common.util.JsonUtils;
import com.atguigu.gmall.common.util.MD5;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.bo.UserIdBo;
import com.atguigu.gmall.model.mq.SeckillOrderMsg;
import com.atguigu.gmall.seckill.service.SeckillCacheService;
import com.atguigu.gmall.seckill.service.biz.SeckillBizService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class SeckillBizServiceImpl implements SeckillBizService {


    @Autowired
    SeckillCacheService seckillCacheService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Override
    public String generateSeckillCode(Long skuId) {

        //1、验用户登录
        UserIdBo bo = AuthContextHolder.getUserIdBo();
        Long userId = bo.getUserId();
        if (userId == null) {
            //用户没登录
            throw new GmallException(ResultCodeEnum.LOGIN_AUTH);
        }


        //2、验商品是否到了秒杀时间以及是否秒杀结束？
        SeckillGoods goodsDetail = seckillCacheService.getGoodsDetail(skuId);


        Date startTime = goodsDetail.getStartTime();
        Date endTime = goodsDetail.getEndTime();
        Date current = new Date();
        if (current.compareTo(startTime) < 0) {
            //当前时间在startTime之前，秒杀还没看开始
            throw new GmallException(ResultCodeEnum.SECKILL_NO_START);
        }

        if (current.compareTo(endTime) > 0) {
            //当前时间在endTime之后，说明秒杀早已结束
            throw new GmallException(ResultCodeEnum.SECKILL_END);
        }

        //3、验商品库存。库存扣json串是非原子操作。
        Integer count = goodsDetail.getStockCount();
        if (count <= 0) {
            throw new GmallException(ResultCodeEnum.SECKILL_FINISH);
        }

        //以上全都验证通过才生成秒杀码？ MD5(skuId+userId+date)
        //验证这个秒杀码的正确性，不用去redis；
        String code = MD5.encrypt(skuId + "" + userId + DateUtil.formatDate(new Date()));

        return code;
    }

    @Override
    public void createSeckillOrder(Long skuId, String code) {
        Date current = new Date();
        UserIdBo bo = AuthContextHolder.getUserIdBo();
        //1、验证秒杀码
        if (!MD5.encrypt(skuId + "" + bo.getUserId() + DateUtil.formatDate(current)).equals(code)) {
            //秒杀码正确
            throw new GmallException(ResultCodeEnum.SECKILL_ILLEGAL);
        }

        //2、验证是否开始秒杀了或者秒杀是否结束了
        SeckillGoods detail = seckillCacheService.getGoodsDetail(skuId);
        if (!(detail.getStartTime().compareTo(current) <= 0 && detail.getEndTime().compareTo(current) >= 0)) {
            //秒杀时间段不合法
            throw new GmallException(ResultCodeEnum.SECKILL_NO_START);
        }


        //3、校验本地内存
        if (detail.getStockCount() <= 0) {
            throw new GmallException(ResultCodeEnum.SECKILL_FINISH);
        }

        //3、防止重复秒杀（同一个秒杀码，指的就是同一个用户买同一个商品）
        String codeLock = RedisConstant.SECKILL_CODE_LOCK + code;

        // setnx key value
        Boolean absent = redisTemplate.opsForValue().setIfAbsent(codeLock, "" + skuId, 1, TimeUnit.DAYS);
        if (!absent) { //说明redis从来没有保存过这个商品的秒杀码
            //排队中。用户已经发过这个请求了，正在进行处理
            return;
        }


        //为了让redis轻松；
        //4、先扣本地内存；更新内存状态位
        detail.setStockCount(detail.getStockCount() - 1); //更新map


        //3、校验本地内存
        if (detail.getStockCount() <= 0) {
            throw new GmallException(ResultCodeEnum.SECKILL_FINISH);
        }


        //4、商品库存实时扣减
        String stockKey = RedisConstant.SECKILL_GOOD_STOCK + DateUtil.formatDate(new Date()) + ":" + skuId;
        Long decrement = redisTemplate.opsForValue().decrement(stockKey);
        if (decrement < 0) {
            //redis减库存失败。秒杀没有名额，不能下单
            throw new GmallException(ResultCodeEnum.SECKILL_FINISH);
        }

        //5、redis扣成功，真正下一个秒杀单；
        SeckillOrderMsg msg = new SeckillOrderMsg(bo.getUserId(), skuId, code, detail);
        rabbitTemplate.convertAndSend(RabbitConstant.ORDER_EVENT_EXCHANGE,
                RabbitConstant.ORDER_SECKILL_RK, JsonUtils.parseObjectToJsonStr(msg));

    }

    @Override
    public ResultCodeEnum checkSeckillOrder(Long skuId) {
        Long userId = AuthContextHolder.getUserIdBo().getUserId();
        //下单码
        String code = MD5.encrypt(skuId + "" + userId + DateUtil.formatDate(new Date()));


        //这个下单码对应的秒杀单状态？
        String key = RedisConstant.SECKILL_CODE_LOCK + code;
        //211  正在排队中
        String jsonStr = redisTemplate.opsForValue().get(key);
        if(!StringUtils.isEmpty(jsonStr)){
            try {
                //1、请求发来了，但是临时订单还没创建
                Long.parseLong(jsonStr);
                return ResultCodeEnum.SECKILL_RUN;
            }catch (Exception e){
                //2、请求发来了，而且临时订单也创建了
                return ResultCodeEnum.SECKILL_SUCCESS;
            }
        }else {
            //已经售罄了
            return ResultCodeEnum.SECKILL_FINISH;
        }

    }
}
